
package com.sausageking.client.consumer.activity;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.inject.Inject;
import com.sausageking.client.ConsumerService.AuthType;
import com.sausageking.client.ConsumerService.MerchantCustomerInfo;
import com.sausageking.client.ConsumerService.RedeemResponse;
import com.sausageking.client.ConsumerServiceAsync;
import com.sausageking.client.common.ClientUtil;
import com.sausageking.client.common.Context;
import com.sausageking.client.consumer.ui.DealView;
import com.sausageking.client.consumer.ui.MerchantView;
import com.sausageking.client.consumer.ui.MerchantView.Presenter;
import com.sausageking.client.consumer.ui.RedeemDialogView;
import com.sausageking.client.consumer.ui.StampDialogView;
import com.sausageking.shared.BuyerException;
import com.sausageking.shared.BuyerException.Code;
import com.sausageking.shared.Checkin;
import com.sausageking.shared.Consumer;
import com.sausageking.shared.Merchant;
import com.sausageking.shared.Offer;
import com.sausageking.shared.Redemption;

public class MerchantPresenterImpl
    implements
      Presenter,
      StampDialogView.Presenter,
      RedeemDialogView.Presenter {

  @Inject
  private ConsumerServiceAsync consumerService;

  @Inject
  StampDialogView stampDialog;

  @Inject
  RedeemDialogView redeemDialog;

  @Inject
  ClientUtil util;

  MerchantView view;

  private Merchant merchant;

  private Context context;

  private List<Checkin> checkins = new ArrayList<Checkin>();

  private List<Redemption> redemptions = new ArrayList<Redemption>();

  ContainerPresenter container;

  public void load(final Context context) {
    this.context = context;
    consumerService.getMerchantCustomerInfo(context.getEmail(),
        context.getMerchant(), new AsyncCallback<MerchantCustomerInfo>() {

          @Override
          public void onFailure(Throwable caught) {
            container.processed();
            // Window.alert(caught.getLocalizedMessage());
          }

          @Override
          public void onSuccess(MerchantCustomerInfo result) {
            merchant = result.getMerchant();
            checkins = filterList(result.getCheckins(),
                context.getKeepLastNCheckins());
            redemptions = filterList(result.getRedemptons(),
                context.getKeepLastNRedemptions());
            Consumer user = result.getUser();
            context.setHasPassword(user != null && user.getPassword() != null);
            context.persist();

            updateView(merchant, checkins, redemptions, context);
            container.processed(); 
          }
        });
  }

  private <T> List<T> filterList(List<T> checkins, int keep) {
    List<T> result = new ArrayList<T>();
    for (int i = 0; i < checkins.size() && i < keep; i++) {
      result.add(checkins.get(i));
    }
    return result;
  }

  protected boolean canCheckin(List<Checkin> checkins) {
    if (checkins.isEmpty()) {
      return true;
    }
    // Test accounts can get stamped once every 5 sec's
    if (util.isTestAccount(checkins.get(0).getUserId())) {
      return new Date().getTime() - checkins.get(0).getCreationTime().getTime() > 5 * 1000;
    }
    return new Date().getTime() - checkins.get(0).getCreationTime().getTime() > util
        .getMinStampIntervalInMs();
  }

  @Override
  public void updateView(Merchant merchant, List<Checkin> checkins,
      List<Redemption> redemptions, Context context) {
    if (checkins == null) {
      checkins = new ArrayList<Checkin>();
    }
    if (redemptions == null) {
      redemptions = new ArrayList<Redemption>();
    }
    view.setMerchant(merchant);
    view.clearDeals();
    Offer offer = merchant.getOffer();
    if (offer == null) {
      return;
    }

    long msTillLastRedemptionExpires = getMsTillLastRedemptionExpires(redemptions);
    int numRedeemables = checkins.size() / offer.getCheckinCount()
        - redemptions.size();

    // render all info., some of which may not apply to certain state thus will
    // be hidden.
    DealView dealView = view
        .addDeal(offer)
        .showStamps(checkins, msTillLastRedemptionExpires > 0,
            numRedeemables > 0)
        .setRedemptionExpires(msTillLastRedemptionExpires)
        .setRedemptionCode("R95A");

    // unknown user
    if (!context.hasEmail()) {
      dealView.setStampable(true).setRedeeming(false).setRedeemable(false);
      return;
    }

    // last redemption hasn't expired yet
    if (msTillLastRedemptionExpires > 0) {
      dealView.setRedeeming(true).setStampable(false).setRedeemable(false);
      return;
    }

    // collected enough stamps to redeem
    if (numRedeemables > 0) {
      dealView.setRedeemable(true).setRedeeming(false).setStampable(false);
      return;
    }

    // not enough stamps to redeem
    dealView.setStampable(true).setRedeeming(false).setRedeemable(false);
  }

  private long getMsTillLastRedemptionExpires(List<Redemption> redemptions) {
    Redemption lastRedemption = getFirstOrNull(redemptions);
    return lastRedemption == null ? Long.MIN_VALUE : util
        .getRedemptionExpirationInMs()
        - (new Date().getTime() - lastRedemption.getCreationTime().getTime());
  }

  private <T> T getFirstOrNull(List<T> list) {
    return (list == null || list.isEmpty()) ? null : list.get(0);
  }

  @Override
  public void setContainerPresenter(ContainerPresenter container) {
    this.container = container;
  }

  @Override
  public void setView(MerchantView view) {
    this.view = view;
  }

  @Override
  public void startStampDialog() {
    stampDialog.setContext(context).setPresenter(this).show();
    if (!canCheckin(checkins)) {
      stampDialog.setError(Code.STAMP_TOO_OFTEN);
      stampDialog.setErrorMode(true);
    }
  }

  @Override
  public void stamp(String merchantId, final String email,
      String orderConfirmationCode) {
    container.processing();
    consumerService.stamp(email, merchantId, orderConfirmationCode,
        new AsyncCallback<Checkin>() {

          @Override
          public void onSuccess(Checkin checkin) {
            checkins.add(0, checkin);
            context.setEmail(email).persist();
            updateView(merchant, checkins, redemptions, context);
            stampDialog.hide();
            container.processed();
          }

          @Override
          public void onFailure(Throwable caught) {
            BuyerException e = (BuyerException) caught;
            stampDialog.setError(e.getCode());
            container.processed();
          }
        });
  }

  @Override
  public void startRedeemDialog() {
    redeemDialog.setContext(context).setPresenter(this).show();
  }

  @Override
  public void redeem(String email, String merchantId, AuthType authType,
      String secret) {
    container.processing();
    consumerService.redeem(email, merchantId, authType, secret,
        new AsyncCallback<RedeemResponse>() {

          @Override
          public void onFailure(Throwable caught) {
            BuyerException e = (BuyerException) caught;
            redeemDialog.setError(e.getCode());
            container.processed();
          }

          @Override
          public void onSuccess(RedeemResponse result) {
            redemptions.add(0, result.getRedemption());
            context.setSessionId(result.getNewSessionId()).persist();
            updateView(merchant, checkins, redemptions, context);
            redeemDialog.hide();
            container.processed();
          }
        });

  }
}
